// 全局的状态定义
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

class MyPromise {
    constructor(executor) {
        // 用try catch来捕获执行器中抛出的错误
        try {
            executor(this.resolve, this.reject)
        } catch (e) {
            this.reject(e);
        }
    }

    // promise 状态，初始化为PENDING
    status = PENDING;
    // 成功回调的值
    value = undefined;
    // 失败后的原因
    reason = undefined;
    // 成功回调
    successCallback = [];
    // 失败回调
    failCallback = [];

    resolve = value => {
        // 状态一旦从PENDING转成其他，那么就不可更改
        if (this.status !== PENDING) return;
        // 修改状态为成功
        this.status = FULFILLED;
        // 保存成功后的值
        this.value = value;
        // 处理一个promise后，多个then的情况，用数组循环调用所有的then
        while (this.successCallback.length) {
            this.successCallback.shift()();
        }
    };

    reject = reason => {
        // 状态一旦从PENDING转成其他，那么就不可更改
        if (this.status !== PENDING) return;
        // 修改状态为失败
        this.status = REJECTED;
        // 保存失败的原因
        this.reason = reason;
        // 循环调用所有的failCallback
        while (this.failCallback.length) {
            this.failCallback.shift()();
        }
    };

    // 这里把resolvePromise放到了class内部
    static resolvePromise(promise2, x, resolve, reject) {
        // 处理循环调用promise的问题
        if (promise2 === x) {
            return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
        }
        if (x instanceof MyPromise) {
            // promise 对象
            x.then(resolve, reject);
        } else {
            // 普通值
            resolve(x);
        }
    }

    then(successCallback, failCallback) {
        // 用于处理参数为空的情况， .then().then()
        successCallback = successCallback ? successCallback : value => value;
        failCallback = failCallback ? failCallback : reason => {
            throw reason
        };

        let promise2 = new MyPromise((resolve, reject) => {
            // 首先判断状态
            if (this.status === FULFILLED) {
                // 用setTimeout来将内部逻辑转成异步，否则promise2这个变量获取不到
                setTimeout(() => {
                    try {
                        let x = successCallback(this.value);
                        MyPromise.resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                }, 0)
            } else if (this.status === REJECTED) {
                setTimeout(() => {
                    try {
                        let x = failCallback(this.reason);
                        MyPromise.resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e);
                    }
                }, 0)
            } else {
                // PENDING
                // 将成功回调和失败回调存储起来
                this.successCallback.push(() => {
                    setTimeout(() => {
                        try {
                            let x = successCallback(this.value);
                            MyPromise.resolvePromise(promise2, x, resolve, reject)
                        } catch (e) {
                            reject(e);
                        }
                    }, 0)
                });
                this.failCallback.push(() => {
                    setTimeout(() => {
                        try {
                            let x = failCallback(this.reason);
                            MyPromise.resolvePromise(promise2, x, resolve, reject)
                        } catch (e) {
                            reject(e);
                        }
                    }, 0)
                });
            }
        });

        return promise2;
    }

    // 注意：由于无法知道promise的最终状态，所以finally的回调函数中不接收任何参数，它仅用于无论最终结果如何都要执行的情况。
    finally(callback) {
        // 返回promise使得能继续使用then
        return this.then((value) => {
            return MyPromise.resolve(callback()).then(() => value);
        }, (reason) => {
            return MyPromise.resolve(callback()).then(() => {
                throw reason
            });

        });
    }

    catch(failCallback) {
        return this.then(undefined, failCallback);
    }

    // allSettled 和 all的主要区别就是，all中，只要有一个reject，就直接返回reject的reason，而allSettled则会全部执行完
    static allSettled(array) {
        let result = [];
        let index = 0;

        return new MyPromise((resolve, reject) => {
            function addData(key, value) {
                result[key] = value;
                index++;
                if (index === array.length) {
                    resolve(result);
                }
            }

            for (let i = 0; i < array.length; i++) {
                let current = array[i];
                if (current instanceof MyPromise) {
                    current.then((value) => {
                        addData(i, value);
                    }, (reason) => {
                        addData(i, reason);
                    });
                } else {
                    // 普通数值直接加入结果数组
                    addData(i, array[i]);
                }
            }
        })
    }

    static all(array) {
        let result = [];
        let index = 0;

        return new MyPromise((resolve, reject) => {
            function addData(key, value) {
                result[key] = value;
                index++;
                // 计数，当index等于array长度时，说明所有的promise都处理完了，可以resolve
                if (index === array.length) {
                    resolve(result);
                }
            }

            for (let i = 0; i < array.length; i++) {
                let current = array[i];
                // 如果是promise对象
                if (current instanceof MyPromise) {
                    current.then((value) => {
                        addData(i, value);
                    }, (reason) => {
                        reject(reason);
                    });
                } else {
                    // 普通数值直接加入结果数组
                    addData(i, array[i]);
                }
            }
        })
    }

    static race(array) {
        return new Promise((resolve, reject) => {
            for (let i = 0; i < array.length; i++) {
                let current = array[i];
                MyPromise.resolve(current).then((res) => resolve(res)).catch(e => reject(e));
            }
        })
    }

    static resolve(value) {
        // 处理value是promise的情况，直接返回
        if (value instanceof MyPromise) {
            return value;
        } else {
            // 如果是普通值，那么封装成promise对象
            return new MyPromise((resolve, reject) => {
                resolve(value)
            });
        }
    }
}

module.exports = MyPromise;
